﻿using HalconDotNet;
using System;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using CameraHandle = System.Int32;

namespace NDK.AcqDevice
{
    /// <summary>
    ///迈德威视
    /// </summary>
    public class MVController : ICarmera
    {
        [System.Runtime.InteropServices.DllImport("winmm")]
        private static extern int timeGetTime();
        #region 1.0 Fields,Properties

        protected CameraHandle _hCamera = -1;//相机句柄
        protected tSdkCameraDevInfo m_DevInfo;
        protected ColorPalette _GrayPal;
        protected IntPtr _ImageBuffer;             // 预览通道RGB图像缓存
        protected tSdkCameraCapbility tCameraCapability;  // 相机特性描述
        protected IntPtr _ImageBufferSnapshot;     // 抓拍通道RGB图像缓存
        private tSdkImageResolution m_tRoiResolution;//用户自定义的分辨率
        private string snId = string.Empty;//相机sn
        protected int _ImgWidth;
        protected int _ImgHeight;
        protected CAMERA_SNAP_PROC _CaptureCallback;
        private AutoResetEvent eventWait = new AutoResetEvent(false);//采集完成信号
        private AutoResetEvent eventWait1 = new AutoResetEvent(false);//采集完成信号
                                                                      //protected ColorPalette m_GrayPal;
        protected IntPtr m_ImageBufferSnapshot;     //!!改写    彩色相机和黑白相机通用
        public event PropertyChangedEventHandler PropertyChanged;
        private string _status = string.Empty;
        private HImage _image = new HImage();
        private Bitmap _image2;    //!!!

        string _SerialNumber = "";
        string _FilePath = "";
        public string SerialNumber { get { return _SerialNumber; } }
        public string FilePath { get { return _FilePath; } }
        public double Exposure
        {
            set { MvApi.CameraSetExposureTime(_hCamera, value); }
            get
            {
                double Exp = 0;
                MvApi.CameraGetExposureTime(_hCamera, ref Exp);
                return Exp;
            }
        }

        public int Width
        {
            set { }
            get
            {
                int pih = 0; int piv = 0; int w = 0; int h = 0;
                MvApi.CameraGetAeWindow(_hCamera, ref pih, ref piv, ref w, ref h);
                return w;
            }
        }
        public int Height
        {
            set { }
            get
            {
                int pih = 0; int piv = 0; int w = 0; int h = 0;
                MvApi.CameraGetAeWindow(_hCamera, ref pih, ref piv, ref w, ref h);
                return h;
            }
        }
        public string CameraPixelFormat { get; set; } = "未连接";
        public string Status
        {
            get
            {
                return _status;
            }
            private set
            {
                _status = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(value.ToString()));
            }
        }

        public ConcurrentQueue<HImage> Queuephoto { get; set; }

        public Bitmap ImageBitmap(int timeout = 3000)        //!!!Bitmap格式暂时不用
        {
            if (eventWait1.WaitOne(timeout) == true)
            {
                if (_image2 != null)
                {
                    return _image2;
                }
            }
            return null;
        }
        #endregion

        #region 3.0 Initialized
        /// <summary>
        /// 构造方法中初始化相关数据
        /// </summary>
        /// <param name="baudRate">端口号</param>
        /// <param name="dataBits">数据位</param>
        public MVController()
        {

        }
        #endregion

        #region 4.0 Public Methods

        /// <summary>
        /// 初始化相机
        /// </summary>
        /// <param name="sn">相机序列号</param>
        /// <param name="mode">0表示连续模式，1是软触发，2是硬触发。</param>
        /// <returns></returns>
        public bool ConnectCamera(string SerialNumber, string FilePath, double exp)
        {
            _SerialNumber = SerialNumber;
            _FilePath = FilePath;
            // 0表示连续模式，1是软触发，2是硬触发
            int mode = 1;
            CameraSdkStatus cameraSdkStatus;
            tSdkCameraDevInfo[] tCameraDevInfoList;
            //if (_hCamera > 0)
            //{
            //    //已经初始化过，先断开
            //    CloseCamera();
            //}

            cameraSdkStatus = MvApi.CameraEnumerateDevice(out tCameraDevInfoList);
            if (cameraSdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
            {
                if (tCameraDevInfoList != null)//此时iCameraCounts返回了实际连接的相机个数
                {
                    var snArray = tCameraDevInfoList.Select(k => this.GetStringByBytes(k.acSn));
                    Status = $"搜索到相机{snArray.Count()}个，SN为：" + string.Join(";", snArray);
                    if (string.IsNullOrEmpty(SerialNumber) && !string.IsNullOrEmpty(this.snId))
                    {
                        SerialNumber = this.snId;
                    }
                    if (!snArray.Contains(SerialNumber))
                    {
                        return false;
                    }
                    this.m_DevInfo = tCameraDevInfoList.Where(k => this.GetStringByBytes(k.acSn) == SerialNumber).Select(k => k).FirstOrDefault();
                    //相机初始化
                    cameraSdkStatus = MvApi.CameraInit(ref this.m_DevInfo, -1, -1, ref _hCamera);
                    if (cameraSdkStatus == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                    {
                        this.snId = SerialNumber;
                        //获得相机特性描述
                        MvApi.CameraGetCapability(_hCamera, out tCameraCapability);

                        _ImageBuffer = Marshal.AllocHGlobal(tCameraCapability.sResolutionRange.iWidthMax * tCameraCapability.sResolutionRange.iHeightMax * 3 + 1024);
                        _ImageBufferSnapshot = Marshal.AllocHGlobal(tCameraCapability.sResolutionRange.iWidthMax * tCameraCapability.sResolutionRange.iHeightMax * 3 + 1024);

                        if (tCameraCapability.sIspCapacity.bMonoSensor != 0)
                        {
                            CameraPixelFormat = "Mono";
                            // 黑白相机输出8位灰度数据
                            MvApi.CameraSetIspOutFormat(_hCamera, (uint)emImageFormat.CAMERA_MEDIA_TYPE_MONO8);
                            //Bitmap 使用
                            //Bitmap Image = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
                            //m_GrayPal = Image.Palette;
                            //for (int Y = 0; Y < m_GrayPal.Entries.Length; Y++)
                            //    m_GrayPal.Entries[Y] = Color.FromArgb(255, Y, Y, Y);
                        }
                        else
                        {
                            CameraPixelFormat = "Color";
                        }
                        tSdkImageResolution imageResolution;
                        MvApi.CameraGetImageResolution(_hCamera, out imageResolution);
                        imageResolution.iIndex = 0;//切换预设分辨率， 只需要设定index值就行了。 
                        MvApi.CameraSetImageResolution(_hCamera, ref imageResolution);
                        MvApi.CameraGetImageResolution(_hCamera, out m_tRoiResolution);
                        m_tRoiResolution.iIndex = 0xff;
                        MvApi.CameraSetTriggerMode(_hCamera, mode);//0表示连续模式，1是软触发，2是硬触发。

                        MvApi.CameraPlay(_hCamera);
                        Status = $"相机{SerialNumber}初始化成功，触发模式：{mode}";


                        CAMERA_SNAP_PROC pCaptureCallOld = null;
                        IntPtr m_iCaptureCallbackCtx = IntPtr.Zero;
                        _ImgWidth = tCameraCapability.sResolutionRange.iWidthMax;
                        _ImgHeight = tCameraCapability.sResolutionRange.iHeightMax;
                        _CaptureCallback = new CAMERA_SNAP_PROC(ImageCaptureCallback);
                        m_ImageBufferSnapshot = Marshal.AllocHGlobal(tCameraCapability.sResolutionRange.iWidthMax * tCameraCapability.sResolutionRange.iHeightMax * 3 + 1024);//!!改写
                        MvApi.CameraPlay(_hCamera);                                       //!!改写
                        cameraSdkStatus = MvApi.CameraSetCallbackFunction(_hCamera, _CaptureCallback, m_iCaptureCallbackCtx, ref pCaptureCallOld);
                    }
                    else
                    {
                        _hCamera = 0;

                        string cause = MvApi.CameraGetErrorString(cameraSdkStatus);
                        Status = $"相机{SerialNumber}初始化错误，错误码：{cameraSdkStatus},错误原因是：{cause}";
                        return false;
                    }
                }
            }
            else
            {
                Status = string.Format("没有找到相机，如果已经接上相机，可能是权限不够，请尝试使用管理员权限运行程序。");
                return false;
            }
            return LoadParameterFromFile(FilePath);
        }

        private void ImageCaptureCallback(int hCamera, IntPtr pFrameBuffer, ref tSdkFrameHead pFrameHead, IntPtr pContext)
        {
            // GC.Collect();
            // IntPtr pFrameBuffer = _ImageBuffer;
            MvApi.CameraImageProcess(hCamera, pFrameBuffer, m_ImageBufferSnapshot, ref pFrameHead);     //!!彩色相机和黑白相机通用
            // MvApi.CameraImageProcess(hCamera, pRawBuffer, pFrameBuffer, ref pFrameHead);
            // 由于SDK输出的数据默认是从底到顶的，转换为Bitmap需要做一下垂直镜像
            MvApi.CameraFlipFrameBuffer(m_ImageBufferSnapshot, ref pFrameHead, 1);

            //_image2 = null;
            try
            {
                #region HImage 
                int w = pFrameHead.iWidth;
                int h = pFrameHead.iHeight;
                Status = string.Format($"Recieve Image Type:{pFrameHead.uiMediaType}");

                if (pFrameHead.uiMediaType == (uint)emImageFormat.CAMERA_MEDIA_TYPE_MONO8)
                {
                    CameraPixelFormat = "Mono";
                    _image = new HImage();
                    _image.GenImage1("byte", w, h, m_ImageBufferSnapshot);
                }
                else if (pFrameHead.uiMediaType == (uint)emImageFormat.CAMERA_MEDIA_TYPE_BGR8)
                {
                    CameraPixelFormat = "Color";
                    _image = new HImage();
                    _image.GenImageInterleaved(
                        m_ImageBufferSnapshot,
                        "bgr",
                        w, h,
                        -1, "byte",
                        w, h,
                        0, 0, -1, 0);
                }
                else
                {
                    // 软件bug
                    _image = null;
                   
                    new HalconException("Image format is not supported!!");
                }
                eventWait.Set();
                #endregion

                //#region Bitmap
                //if (pFrameHead.uiMediaType == (uint)MVSDK.emImageFormat.CAMERA_MEDIA_TYPE_MONO8)
                //{
                //    int w1 = pFrameHead.iWidth;
                //    int h1 = pFrameHead.iHeight;
                //    _image2 = null;

                //    Boolean gray = (pFrameHead.uiMediaType == (uint)MVSDK.emImageFormat.CAMERA_MEDIA_TYPE_MONO8);
                //    Bitmap Image = new Bitmap(w1, h1,
                //        gray ? w1 : w1 * 3,
                //        gray ? PixelFormat.Format8bppIndexed : PixelFormat.Format24bppRgb,
                //        pFrameBuffer);
                //    _image2 = new Bitmap(Image);
                //    eventWait1.Set();
                //}

                //// 如果是灰度图要设置调色板
                //if (gray)
                //{
                //    Image.Palette = m_GrayPal;
                //}

                //#endregion
            }
            catch (HalconException ex)
            {
                throw ex;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// 获取相机连线状态
        /// </summary>
        /// <returns>true:连接；false：未连接</returns>
        public bool IsConnected
        {
            get
            {
                return _hCamera == -1 ? false : MvApi.CameraConnectTest(this._hCamera) == CameraSdkStatus.CAMERA_STATUS_SUCCESS;
            }
        }

        /// <summary>
        /// 关闭相机
        /// </summary>
        /// <returns></returns>
        public bool CloseCamera()
        {
            try
            {
                if (_hCamera > 0)
                {
                    var status = MvApi.CameraUnInit(this._hCamera);
                    if (_ImageBuffer != IntPtr.Zero) Marshal.FreeHGlobal(_ImageBuffer);
                    if (_ImageBufferSnapshot != IntPtr.Zero) Marshal.FreeHGlobal(_ImageBufferSnapshot);
                    if (status == CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                    {
                        _hCamera = -1;
                        return true;
                    }
                    else
                    {
                        Status = $"相机{this.snId}关闭失败，错误码：{(int)status},错误原因是：{status.ToString()}";
                        throw new Exception($"{this.snId} 关闭失败 ，Error Code:{(int)status}; Error Message：{status.ToString()}");
                    }
                }
            }
            catch (Exception ex)
            {

                throw ex;
            }
            return true;
        }

        /// <summary>
        /// 配置界面
        /// </summary>
        /// <param name="uiIntPtr"></param>
        /// <returns></returns>
        public void SetConfigUI(IntPtr uiIntPtr)
        {
            //让SDK来根据相机的型号动态创建该相机的配置窗口。
            MvApi.CameraCreateSettingPage(_hCamera, uiIntPtr, this.m_DevInfo.acFriendlyName,/*SettingPageMsgCalBack*/null,/*m_iSettingPageMsgCallbackCtx*/(IntPtr)null, 0);
        }
        /// <summary>
        /// 显示配置UI
        /// </summary>
        public void ShowSettingUI()
        {
            MvApi.CameraShowSettingPage(_hCamera, 1);//1 show ; 0 hide
        }

        /// <summary>
        ///软触发相机拍照
        /// </summary>
        /// <returns></returns>
        public HImage GrabImage(double Ep, bool IsChangeGetImageMode = false)
        {
            try
            {
                eventWait.Reset();
                CameraSdkStatus res = MvApi.CameraSoftTrigger(_hCamera);
                if (res != CameraSdkStatus.CAMERA_STATUS_SUCCESS)
                {
                    Status = $"相机{this.snId}关闭失败，错误码：{(int)res},错误原因是：{res.ToString()}";
                    throw new Exception($"相机抓图触发失败，ErrorCode：{res.ToString()}");
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            if (eventWait.WaitOne(3000) == true)
            {
                return _image;
            }
            return null;
        }

        /// <summary>
        /// 装载文件参数
        /// </summary>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public bool LoadParameterFromFile(string filePath)
        {
            var pathFileTem = Path.Combine(Environment.CurrentDirectory, filePath);
            if (!File.Exists(pathFileTem))
            {
                return true;
            }
            return MvApi.CameraReadParameterFromFile(_hCamera, pathFileTem) == CameraSdkStatus.CAMERA_STATUS_SUCCESS;
        }
        #endregion

        #region 5.0 Private Methods

        /// <summary>
        /// 延时
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public void Sleep(int time)
        {
            try
            {
                long interval;
                int oldTime = timeGetTime();
                do
                {
                    System.Windows.Forms.Application.DoEvents();
                    interval = timeGetTime() - oldTime;
                    if (interval < 0) interval = (long)Math.Pow(2, 32) - Math.Abs(interval);
                } while (interval < time);
            }
            catch (System.Exception ex) { MessageBox.Show(ex.Message); }
        }
        /// <summary>
        /// Byte[]ToString
        /// </summary>
        /// <returns></returns>
        private string GetStringByBytes(byte[] sourceData)
        {
            var res2 = new ASCIIEncoding().GetString(sourceData, 0, sourceData.Length);
            return res2.Substring(0, res2.IndexOf('\0'));
        }
        #endregion
    }
}
